package com.yuewen.mall.common.redisclient;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import redis.clients.jedis.exceptions.JedisConnectionException;
import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.jedis.exceptions.JedisException;

import javax.xml.bind.ValidationEvent;
import java.io.*;

/**
 * Created by liwei.b on 2016/6/15.
 */
public class RedisClient {
    private boolean isAvailable = false;
    private JedisPool pool;
    private String redisHost;
    private int port;
    private String password;
    private Jedis jedis;

    public RedisClient() {

    }

    public RedisClient(String theRedisHost, int thePort) {
        this.redisHost = theRedisHost;
        this.port = thePort;
        pool = getPool(theRedisHost, thePort);
        isAvailable = true;
    }

    public RedisClient(String theRedisHost, int thePort, String thePassword) {
        this.redisHost = theRedisHost;
        this.port = thePort;
        if (thePassword != null && thePassword != "") {
            this.password = thePassword;
            pool = getPool(theRedisHost, thePort, thePassword);
        }
        isAvailable = true;
    }

    public boolean isKeyExist(String key) {
        byte[] keys = toByteArray(key);
        boolean flag = false;
        boolean isJedisBreaken = false;
        jedis = getAvailableJedis();
        if (jedis == null) {
            return false;
        }
        try {
            flag = jedis.exists(keys);
        } catch (JedisException ex) {
            isJedisBreaken = handleJedisException(ex);
            flag = false;
        } finally {
            releaseJedisResource(pool, jedis, isJedisBreaken);
        }
        return flag;
    }

    public String setObject(String key, Object value) {
        if (!this.isAvailable) {
            System.out.println("redis server is not inited right");
            return "";
        }
        if (value == null) {
            System.out.println("redis value can not be null or empty");
            return "";
        }
        byte[] keys = toByteArray(key);
        byte[] values = toByteArray(value);
        if (keys == null || values == null) {
            return "";
        }
        String strResult = "";
        boolean isJedisBreaken = false;
        jedis = getAvailableJedis();
        if (jedis == null) {
            return strResult;
        }
        try {

            if (jedis.exists(keys)) {
                jedis.del(keys);
            }
            strResult = jedis.setex(keys, 8640000, values);//100天
        } catch (JedisException ex) {
            isJedisBreaken = handleJedisException(ex);
            strResult = "";
        } finally {
            releaseJedisResource(pool, jedis, isJedisBreaken);
        }
        return strResult;
    }

    public String setObject(String key, Object value, int expiredWithSecnds) {
        if (!this.isAvailable) {
            System.out.println("redis server is not inited right");
            return "";
        }
        if (value == null) {
            System.out.println("redis value can not be null or empty");
            return "";
        }
        byte[] keys = toByteArray(key);
        byte[] values = toByteArray(value);
        if (keys == null || values == null) {
            return "";
        }
        String strResult = "";
        boolean isJedisBreaken = false;
        jedis = getAvailableJedis();
        if (jedis == null) {
            return strResult;
        }
        try {
            if (jedis.exists(keys)) {
                jedis.del(keys);
            }
            strResult = jedis.setex(keys, expiredWithSecnds, values);
        } catch (JedisException ex) {
            isJedisBreaken = handleJedisException(ex);
            strResult = "";
        } finally {
            releaseJedisResource(pool, jedis, isJedisBreaken);
        }
        return strResult;
    }

    public Object get(String key) {
        if (!this.isAvailable) {
            return null;
        }
        byte[] keys = toByteArray(key);
        if (keys == null) {
            return null;
        }
        Object obj = null;
        boolean isJedisBreaken = false;
        jedis = getAvailableJedis();
        if (jedis == null) {
            return obj;
        }
        try {
            obj = toObject(jedis.get(keys));
        } catch (JedisException ex) {
            isJedisBreaken = handleJedisException(ex);
        } finally {
            releaseJedisResource(pool, jedis, isJedisBreaken);
        }
        if (obj == null) {
            return null;
        }
        return obj;
    }

    public <T> T get(Class<T> dataType, String key) {
        if (!this.isAvailable) {
            return null;
        }
        byte[] keys = toByteArray(key);
        if (keys == null) {
            return null;
        }
        Object obj = null;
        boolean isJedisBreaken = false;
        jedis = getAvailableJedis();
        if (jedis == null) {
            return null;
        }
        try {

            obj = toObject(jedis.get(keys));
        } catch (JedisException ex) {
            isJedisBreaken = handleJedisException(ex);
        } finally {
            releaseJedisResource(pool, jedis, isJedisBreaken);
        }
        if (obj == null) {
            return null;
        }
        return (T) obj;
    }

    public long delete(String key) {
        if (!this.isAvailable) {
            System.out.println("redis server is not inited right");
            return 0;
        }
        byte[] keys = toByteArray(key);
        if (keys == null) {
            return 0;
        }
        long result = 0;
        boolean isJedisBreaken = false;
        jedis = getAvailableJedis();
        if (jedis == null) {
            return result;
        }
        try {
            result = jedis.del(keys);
        } catch (JedisException ex) {
            isJedisBreaken = handleJedisException(ex);
        } finally {
            releaseJedisResource(pool, jedis, isJedisBreaken);
        }
        return result;
    }

    private byte[] toByteArray(Object obj) {
        if (obj == null) {
            return null;
        }
        byte[] bytes = null;
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
            oos.close();
            bos.close();
        } catch (IOException ex) {
            System.out.println(obj.getClass().getName() + " cannot be serializable. it must implement Serializable");
            ex.printStackTrace();
        }
        return bytes;
    }

    private Object toObject(byte[] bytes) {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        Object obj = null;
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bis);
            obj = ois.readObject();
            ois.close();
            bis.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        } catch (ClassNotFoundException ex) {
            ex.printStackTrace();
        }
        return obj;
    }

    private JedisPool getPool(String host, int port, String password) {
        if (pool == null) {
            JedisPoolConfig config = new JedisPoolConfig();
            //控制一个pool可分配多少个jedis实例，通过pool.getResource()来获取；
            //如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。
            config.setMaxTotal(500);
            //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
            config.setMaxIdle(5);
            //表示当borrow(引入)一个jedis实例时，最大的等待时间，如果超过等待时间，则直接抛出JedisConnectionException；
            config.setMaxWaitMillis(1000 * 100);
            //在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
            config.setTestOnBorrow(true);
            config.setTestOnReturn(true);
            pool = new JedisPool(config, host, port, 5000, password);
        }
        return pool;
    }

    private JedisPool getPool(String host, int port) {
        if (pool == null) {
            JedisPoolConfig config = new JedisPoolConfig();
            //控制一个pool可分配多少个jedis实例，通过pool.getResource()来获取；
            //如果赋值为-1，则表示不限制；如果pool已经分配了maxActive个jedis实例，则此时pool的状态为exhausted(耗尽)。
            config.setMaxTotal(500);
            //控制一个pool最多有多少个状态为idle(空闲的)的jedis实例。
            config.setMaxIdle(5);
            //表示当borrow(引入)一个jedis实例时，最大的等待时间，如果超过等待时间，则直接抛出JedisConnectionException；
            config.setMaxWaitMillis(1000 * 100);
            //在borrow一个jedis实例时，是否提前进行validate操作；如果为true，则得到的jedis实例均是可用的；
            config.setTestOnBorrow(true);
            config.setTestOnReturn(true);
            pool = new JedisPool(config, host, port);
        }
        return pool;
    }

    private Jedis getAvailableJedis() {
        try {
            isAvailable = true;
            return pool.getResource();
        } catch (Exception ex) {
            isAvailable = false;
            System.out.println("warning: redis pool can not return jedis, maybe your password not right.");
            ex.printStackTrace();
        }
        return null;
    }

    public static void releaseJedisResource(JedisPool pool, Jedis redis, boolean isJedisBreaken) {
        if (redis != null) {
            if (isJedisBreaken) {
                pool.returnBrokenResource(redis);
            } else {
                pool.returnResource(redis);
            }
        }
    }

    protected boolean handleJedisException(JedisException jedisException) {
        jedisException.printStackTrace();
        if (jedisException instanceof JedisConnectionException) {
            System.out.println("Redis connection lost.");
        } else if (jedisException instanceof JedisDataException) {
            if ((jedisException.getMessage() != null) && (jedisException.getMessage().indexOf("READONLY") != -1)) {
                System.out.println("Redis connection are read-only slave.");
            } else {
                // dataException, isBroken=false
                return false;
            }
        } else {
            System.out.println("Jedis exception happen.");
        }
        return true;
    }
}
